package com.zenithmind.exam.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zenithmind.common.annotation.DataCache;
import com.zenithmind.common.context.UserContext;
import com.zenithmind.common.exception.BusinessException;
import com.zenithmind.exam.mapper.ExamMapper;
import com.zenithmind.exam.mapper.ExamQuestionRelMapper;
import com.zenithmind.exam.pojo.dto.ExamCreateDTO;
import com.zenithmind.exam.pojo.entity.Exam;
import com.zenithmind.exam.pojo.entity.ExamQuestionRel;
import com.zenithmind.exam.pojo.vo.ExamVO;
import com.zenithmind.exam.pojo.vo.QuestionVO;
import com.zenithmind.exam.service.ExamService;
import com.zenithmind.exam.service.ExamRecordService;
import com.zenithmind.exam.service.ExamParticipantService;
import com.zenithmind.exam.service.QuestionService;

import java.math.BigDecimal;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 考试服务实现类
 * 
 * @author ZenithMind
 * @since 2024-07-01
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements ExamService {

    private final ExamMapper examMapper;
    private final ExamQuestionRelMapper examQuestionRelMapper;
    private final QuestionService questionService;
    private final ExamRecordService examRecordService;
    private final ExamParticipantService examParticipantService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean createExam(ExamCreateDTO examCreateDTO) {
        try {
            Exam exam = new Exam();
            BeanUtils.copyProperties(examCreateDTO, exam);
            
            // 设置创建者信息
            String currentUserId = UserContext.getUserId();
            String currentUserName = UserContext.getUsername();
            exam.setCreatorId(currentUserId);
            exam.setCreatorName(currentUserName);
            
            // 设置默认值
            if (exam.getStatus() == null) {
                exam.setStatus(0); // 默认草稿状态
            }
            if (exam.getDurationMinutes() == null) {
                exam.setDurationMinutes(120); // 默认120分钟
            }
            if (exam.getPassScore() == null) {
                exam.setPassScore(60); // 默认60分及格
            }
            
            // 设置布尔值
            exam.setIsShuffleQuestions(examCreateDTO.getIsShuffleQuestions() ? 1 : 0);
            exam.setIsShuffleOptions(examCreateDTO.getIsShuffleOptions() ? 1 : 0);
            exam.setAllowReview(examCreateDTO.getAllowReview() ? 1 : 0);
            exam.setShowScore(examCreateDTO.getShowScore() ? 1 : 0);
            exam.setAutoSubmit(examCreateDTO.getAutoSubmit() ? 1 : 0);
            
            // 初始化统计字段
            exam.setParticipantCount(0L);
            exam.setSubmittedCount(0L);
            exam.setAvgScore(BigDecimal.ZERO);
            exam.setMaxScore(BigDecimal.ZERO);
            exam.setMinScore(BigDecimal.ZERO);
            exam.setPassRate(BigDecimal.ZERO);

            boolean result = save(exam);
            if (result && !CollectionUtils.isEmpty(examCreateDTO.getQuestions())) {
                // 保存考试题目关联
                saveExamQuestions(exam.getId(), examCreateDTO.getQuestions());
                
                // 计算总分和题目数量
                updateExamScoreAndCount(exam.getId());
            }
            
            if (result) {
                log.info("创建考试成功，考试ID: {}, 考试标题: {}", exam.getId(), exam.getTitle());
            }
            return result;
        } catch (Exception e) {
            log.error("创建考试失败", e);
            throw new BusinessException("创建考试失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateExam(String examId, ExamCreateDTO examCreateDTO) {
        try {
            Exam existingExam = getById(examId);
            if (existingExam == null) {
                throw new BusinessException("考试不存在");
            }

            // 检查权限
            String currentUserId = UserContext.getUserId();
            if (!existingExam.getCreatorId().equals(currentUserId) && !UserContext.hasRole("ADMIN")) {
                throw new BusinessException("无权限修改此考试");
            }

            // 检查考试状态
            if (existingExam.getStatus() > 1) {
                throw new BusinessException("考试已开始或结束，无法修改");
            }

            Exam exam = new Exam();
            BeanUtils.copyProperties(examCreateDTO, exam);
            exam.setId(examId);
            
            // 设置布尔值
            exam.setIsShuffleQuestions(examCreateDTO.getIsShuffleQuestions() ? 1 : 0);
            exam.setIsShuffleOptions(examCreateDTO.getIsShuffleOptions() ? 1 : 0);
            exam.setAllowReview(examCreateDTO.getAllowReview() ? 1 : 0);
            exam.setShowScore(examCreateDTO.getShowScore() ? 1 : 0);
            exam.setAutoSubmit(examCreateDTO.getAutoSubmit() ? 1 : 0);

            boolean result = updateById(exam);
            if (result && !CollectionUtils.isEmpty(examCreateDTO.getQuestions())) {
                // 删除原有题目关联
                examQuestionRelMapper.deleteByExamId(examId);
                
                // 保存新的题目关联
                saveExamQuestions(examId, examCreateDTO.getQuestions());
                
                // 更新总分和题目数量
                updateExamScoreAndCount(examId);
            }
            
            if (result) {
                log.info("更新考试成功，考试ID: {}, 考试标题: {}", examId, exam.getTitle());
            }
            return result;
        } catch (Exception e) {
            log.error("更新考试失败", e);
            throw new BusinessException("更新考试失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteExam(String examId) {
        try {
            Exam existingExam = getById(examId);
            if (existingExam == null) {
                throw new BusinessException("考试不存在");
            }

            // 检查权限
            String currentUserId = UserContext.getUserId();
            if (!existingExam.getCreatorId().equals(currentUserId) && !UserContext.hasRole("ADMIN")) {
                throw new BusinessException("无权限删除此考试");
            }

            // 检查考试状态
            if (existingExam.getStatus() > 1) {
                throw new BusinessException("考试已开始或结束，无法删除");
            }

            // 删除考试题目关联
            examQuestionRelMapper.deleteByExamId(examId);
            
            boolean result = removeById(examId);
            if (result) {
                log.info("删除考试成功，考试ID: {}", examId);
            }
            return result;
        } catch (Exception e) {
            log.error("删除考试失败", e);
            throw new BusinessException("删除考试失败: " + e.getMessage());
        }
    }

    @Override
    @DataCache(key = "exam:info:#{examId}", expireTime = 30, timeUnit = TimeUnit.MINUTES)
    public ExamVO getExamById(String examId) {
        Exam exam = getById(examId);
        if (exam == null) {
            return null;
        }
        return convertToVO(exam);
    }

    @Override
    public IPage<ExamVO> getExamPage(Page<Exam> page, String title, String categoryId, 
                                    String creatorId, Integer type, Integer status) {
        IPage<Exam> examPage = examMapper.selectExamPage(page, title, categoryId, creatorId, type, status);
        
        IPage<ExamVO> voPage = new Page<>();
        BeanUtils.copyProperties(examPage, voPage);
        
        List<ExamVO> voList = examPage.getRecords().stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
        voPage.setRecords(voList);
        
        return voPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean publishExam(String examId) {
        try {
            Exam exam = getById(examId);
            if (exam == null) {
                throw new BusinessException("考试不存在");
            }

            if (exam.getStatus() != 0) {
                throw new BusinessException("只有草稿状态的考试才能发布");
            }

            // 检查考试是否有题目
            Integer questionCount = examQuestionRelMapper.countByExamId(examId);
            if (questionCount == 0) {
                throw new BusinessException("考试没有题目，无法发布");
            }

            int result = examMapper.updateStatus(examId, 1);
            if (result > 0) {
                log.info("发布考试成功，考试ID: {}", examId);
            }
            return result > 0;
        } catch (Exception e) {
            log.error("发布考试失败", e);
            throw new BusinessException("发布考试失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean pauseExam(String examId) {
        try {
            int result = examMapper.updateStatus(examId, 4);
            if (result > 0) {
                log.info("暂停考试成功，考试ID: {}", examId);
            }
            return result > 0;
        } catch (Exception e) {
            log.error("暂停考试失败", e);
            throw new BusinessException("暂停考试失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean resumeExam(String examId) {
        try {
            int result = examMapper.updateStatus(examId, 2);
            if (result > 0) {
                log.info("恢复考试成功，考试ID: {}", examId);
            }
            return result > 0;
        } catch (Exception e) {
            log.error("恢复考试失败", e);
            throw new BusinessException("恢复考试失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean endExam(String examId) {
        try {
            int result = examMapper.updateStatus(examId, 3);
            if (result > 0) {
                log.info("结束考试成功，考试ID: {}", examId);
                // 更新考试统计信息
                updateExamStatistics(examId);
            }
            return result > 0;
        } catch (Exception e) {
            log.error("结束考试失败", e);
            throw new BusinessException("结束考试失败: " + e.getMessage());
        }
    }

    @Override
    public List<QuestionVO> getExamQuestions(String examId, String userId, boolean showAnswer) {
        List<ExamQuestionRel> questionRels = examQuestionRelMapper.selectByExamId(examId);
        
        return questionRels.stream()
                .map(rel -> {
                    QuestionVO question = questionService.getQuestionById(rel.getQuestionId(), showAnswer);
                    if (question != null) {
                        question.setScore(rel.getScore()); // 使用考试中设置的分值
                    }
                    return question;
                })
                .filter(question -> question != null)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String startExam(String examId, String userId) {
        try {
            Exam exam = getById(examId);
            if (exam == null) {
                throw new BusinessException("考试不存在");
            }

            if (!canUserTakeExam(examId, userId)) {
                throw new BusinessException("用户无法参加此考试");
            }

            // 获取用户信息
            String userName = UserContext.getUsername();
            if (userName == null) {
                userName = "未知用户";
            }

            // 获取IP地址和用户代理（这里简化处理）
            String ipAddress = "127.0.0.1"; // 实际应该从请求中获取
            String userAgent = "Unknown"; // 实际应该从请求中获取

            // 创建考试记录
            String examRecordId = examRecordService.startExam(examId, userId, userName, ipAddress, userAgent);
            
            log.info("用户开始考试，考试ID: {}, 用户ID: {}, 记录ID: {}", examId, userId, examRecordId);
            return examRecordId;
        } catch (Exception e) {
            log.error("开始考试失败", e);
            throw new BusinessException("开始考试失败: " + e.getMessage());
        }
    }

    @Override
    public boolean canUserTakeExam(String examId, String userId) {
        Exam exam = getById(examId);
        if (exam == null) {
            return false;
        }

        // 检查考试状态
        if (exam.getStatus() != 1 && exam.getStatus() != 2) {
            return false;
        }

        // 检查考试时间
        LocalDateTime now = LocalDateTime.now();
        if (exam.getStartTime() != null && now.isBefore(exam.getStartTime())) {
            return false;
        }
        if (exam.getEndTime() != null && now.isAfter(exam.getEndTime())) {
            return false;
        }

        // 检查用户是否在参与者列表中
        if (!examParticipantService.isUserParticipant(examId, userId)) {
            log.warn("用户不在考试参与者列表中，考试ID: {}, 用户ID: {}", examId, userId);
            return false;
        }

        // 检查用户是否已经参加过考试（根据考试类型判断是否允许重复参加）
        // 考试类型：1:练习(允许重复), 2:作业(不允许重复), 3:考试(不允许重复), 4:竞赛(不允许重复)
        if (exam.getType() != null && exam.getType() != 1) {
            // 非练习类型，检查是否已有考试记录
            if (examRecordService.hasUserTakenExam(examId, userId)) {
                log.warn("用户已经参加过此考试且不允许重复参加，考试ID: {}, 用户ID: {}, 考试类型: {}",
                        examId, userId, exam.getType());
                return false;
            }
        }

        return true;
    }

    @Override
    @DataCache(key = "exam:ongoing", expireTime = 5, timeUnit = TimeUnit.MINUTES)
    public List<ExamVO> getOngoingExams() {
        List<Exam> exams = examMapper.selectOngoingExams();
        return exams.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }

    @Override
    @DataCache(key = "exam:upcoming", expireTime = 10, timeUnit = TimeUnit.MINUTES)
    public List<ExamVO> getUpcomingExams() {
        List<Exam> exams = examMapper.selectUpcomingExams();
        return exams.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }

    @Override
    @DataCache(key = "exam:hot:#{limit}", expireTime = 10, timeUnit = TimeUnit.MINUTES)
    public List<ExamVO> getHotExams(Integer limit) {
        List<Exam> exams = examMapper.selectHotExams(limit);
        return exams.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean copyExam(String examId, String newTitle) {
        try {
            Exam sourceExam = getById(examId);
            if (sourceExam == null) {
                throw new BusinessException("源考试不存在");
            }

            Exam newExam = new Exam();
            BeanUtils.copyProperties(sourceExam, newExam);
            newExam.setId(null); // 清空ID，让系统自动生成
            newExam.setTitle(newTitle);
            newExam.setStatus(0); // 新考试为草稿状态
            
            // 重置统计字段
            newExam.setParticipantCount(0L);
            newExam.setSubmittedCount(0L);
            newExam.setAvgScore(BigDecimal.ZERO);
            newExam.setMaxScore(BigDecimal.ZERO);
            newExam.setMinScore(BigDecimal.ZERO);
            newExam.setPassRate(BigDecimal.ZERO);
            
            // 设置创建者信息
            String currentUserId = UserContext.getUserId();
            String currentUserName = UserContext.getUsername();
            newExam.setCreatorId(currentUserId);
            newExam.setCreatorName(currentUserName);

            boolean result = save(newExam);
            if (result) {
                // 复制考试题目关联
                List<ExamQuestionRel> sourceQuestionRels = examQuestionRelMapper.selectByExamId(examId);
                if (!CollectionUtils.isEmpty(sourceQuestionRels)) {
                    List<ExamQuestionRel> newQuestionRels = sourceQuestionRels.stream()
                            .map(rel -> {
                                ExamQuestionRel newRel = new ExamQuestionRel();
                                BeanUtils.copyProperties(rel, newRel);
                                newRel.setId(null);
                                newRel.setExamId(newExam.getId());
                                return newRel;
                            })
                            .collect(Collectors.toList());
                    
                    examQuestionRelMapper.batchInsert(newQuestionRels);
                }
                
                log.info("复制考试成功，源考试ID: {}, 新考试ID: {}", examId, newExam.getId());
            }
            return result;
        } catch (Exception e) {
            log.error("复制考试失败", e);
            throw new BusinessException("复制考试失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateExamStatistics(String examId) {
        try {
            // 获取考试信息
            Exam exam = examMapper.selectById(examId);
            if (exam == null) {
                log.warn("考试不存在，无法更新统计信息，考试ID: {}", examId);
                return;
            }

            // 获取考试统计数据
            Map<String, Object> statistics = examRecordService.getExamStatistics(examId);

            // 更新考试统计信息
            Exam updateExam = new Exam();
            updateExam.setId(examId);
            updateExam.setParticipantCount((Long) statistics.get("participantCount"));
            updateExam.setSubmittedCount((Long) statistics.get("submittedCount"));
            updateExam.setAvgScore((BigDecimal) statistics.get("avgScore"));
            updateExam.setMaxScore((BigDecimal) statistics.get("maxScore"));
            updateExam.setMinScore((BigDecimal) statistics.get("minScore"));
            updateExam.setPassRate((BigDecimal) statistics.get("passRate"));

            examMapper.updateById(updateExam);
            log.info("更新考试统计信息完成，考试ID: {}, 参与人数: {}, 提交人数: {}, 平均分: {}",
                    examId, statistics.get("participantCount"), statistics.get("submittedCount"), statistics.get("avgScore"));
        } catch (Exception e) {
            log.error("更新考试统计信息失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void autoStartExams() {
        try {
            List<Exam> examsToStart = examMapper.selectExamsToStart();
            for (Exam exam : examsToStart) {
                examMapper.updateStatus(exam.getId(), 2);
                log.info("自动开始考试，考试ID: {}", exam.getId());
            }
        } catch (Exception e) {
            log.error("自动开始考试失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void autoEndExams() {
        try {
            List<Exam> examsToEnd = examMapper.selectExamsToEnd();
            for (Exam exam : examsToEnd) {
                examMapper.updateStatus(exam.getId(), 3);
                updateExamStatistics(exam.getId());
                log.info("自动结束考试，考试ID: {}", exam.getId());
            }
        } catch (Exception e) {
            log.error("自动结束考试失败", e);
        }
    }

    @Override
    public List<ExamVO> getUserCreatedExams(String creatorId) {
        List<Exam> exams = examMapper.selectByCreatorId(creatorId);
        return exams.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchDeleteExams(List<String> examIds) {
        try {
            // 检查每个考试是否可以删除
            for (String examId : examIds) {
                Exam exam = getById(examId);
                if (exam != null && exam.getStatus() > 1) {
                    throw new BusinessException("考试 " + examId + " 已开始或结束，无法删除");
                }
            }

            // 删除考试题目关联
            for (String examId : examIds) {
                examQuestionRelMapper.deleteByExamId(examId);
            }
            
            boolean result = removeByIds(examIds);
            if (result) {
                log.info("批量删除考试成功，考试数量: {}", examIds.size());
            }
            return result;
        } catch (Exception e) {
            log.error("批量删除考试失败", e);
            throw new BusinessException("批量删除考试失败: " + e.getMessage());
        }
    }

    /**
     * 保存考试题目关联
     */
    private void saveExamQuestions(String examId, List<ExamCreateDTO.ExamQuestionDTO> questions) {
        List<ExamQuestionRel> questionRels = questions.stream()
                .map(questionDTO -> {
                    ExamQuestionRel rel = new ExamQuestionRel();
                    rel.setExamId(examId);
                    rel.setQuestionId(questionDTO.getQuestionId());
                    rel.setQuestionOrder(questionDTO.getQuestionOrder());
                    rel.setScore(questionDTO.getScore());
                    rel.setCreateTime(LocalDateTime.now());
                    return rel;
                })
                .collect(Collectors.toList());
        
        examQuestionRelMapper.batchInsert(questionRels);
    }

    /**
     * 更新考试总分和题目数量
     */
    private void updateExamScoreAndCount(String examId) {
        Integer totalScore = examQuestionRelMapper.sumScoreByExamId(examId);
        Integer questionCount = examQuestionRelMapper.countByExamId(examId);
        
        Exam exam = new Exam();
        exam.setId(examId);
        exam.setTotalScore(totalScore);
        exam.setQuestionCount(questionCount);
        updateById(exam);
    }

    /**
     * 转换为VO对象
     */
    private ExamVO convertToVO(Exam exam) {
        ExamVO vo = new ExamVO();
        BeanUtils.copyProperties(exam, vo);
        
        // 设置类型名称
        vo.setTypeName(getExamTypeName(exam.getType()));
        
        // 设置状态名称
        vo.setStatusName(getExamStatusName(exam.getStatus()));
        
        // 设置布尔值
        vo.setIsShuffleQuestions(exam.getIsShuffleQuestions() == 1);
        vo.setIsShuffleOptions(exam.getIsShuffleOptions() == 1);
        vo.setAllowReview(exam.getAllowReview() == 1);
        vo.setShowScore(exam.getShowScore() == 1);
        vo.setAutoSubmit(exam.getAutoSubmit() == 1);
        
        return vo;
    }

    /**
     * 获取考试类型名称
     */
    private String getExamTypeName(Integer type) {
        switch (type) {
            case 1: return "练习";
            case 2: return "作业";
            case 3: return "考试";
            case 4: return "竞赛";
            default: return "未知";
        }
    }

    /**
     * 获取考试状态名称
     */
    private String getExamStatusName(Integer status) {
        switch (status) {
            case 0: return "草稿";
            case 1: return "已发布";
            case 2: return "进行中";
            case 3: return "已结束";
            case 4: return "已暂停";
            default: return "未知";
        }
    }
}
